home *** CD-ROM | disk | FTP | other *** search
/ Amiga Collections: Panorama / Panorama - Disk 01 (1986-02-15)(Pacific North-West Amigas Club)[WB].zip / Panorama - Disk 01 (1986-02-15)(Pacific North-West Amigas Club)[WB].adf / aterm5.c < prev    next >
Text File  |  1985-11-26  |  42KB  |  1,379 lines

  1. /************************************************************************
  2.  *  a terminal program that has ascii and xmodem transfer capability
  3.  *
  4.  *  use esc to abort xmodem transfer
  5.  *
  6.  *  written by Michael Mounier
  7.  *
  8.  *  Modification History -
  9.  *   December 85 - added 80 * 25 capabilities  - Larry Phillips
  10.  *   Feb 9 1986  - added Half-duplex and Echoplex
  11.  *               - added timing to and fixed bugs in XMODEM receive
  12.  *               - fixed bug in Ascii send -- now doesn't wait for EVENT
  13.  *                 to send next characters - Steve Allen
  14.  ************************************************************************/
  15.  
  16.  #define TESTING 1
  17.  /*  compiler directives to fetch the necessary header files */
  18.  #include <exec/types.h>
  19.  #include <exec/exec.h>
  20.  #include <intuition/intuition.h>
  21.  #include <intuition/intuitionbase.h>
  22.  #include <graphics/gfxbase.h>
  23.  #include <graphics/gfx.h>
  24.  #include <graphics/text.h>
  25.  #include <graphics/regions.h>
  26.  #include <graphics/copper.h>
  27.  #include <graphics/gels.h>
  28.  #include <devices/serial.h>
  29.  #include <devices/keymap.h>
  30.  #include <hardware/blit.h>
  31.  #include <stdio.h>
  32.  #include <ctype.h>
  33.  #include <libraries/dos.h>
  34.  
  35.  #define INTUITION_REV 1
  36.  #define GRAPHICS_REV  1
  37.  
  38.  /* things for xmodem send and receive */
  39.  #define SECSIZ   128             /* This many bytes in an xmodem block */
  40.  #define BufSize  SECSIZ*2  /* We'll follow two blocks (for chop) */
  41.  #define ERRORMAX 10        /* Max errors before abort */
  42.  #define RETRYMAX 10       /* Maximum retrys before abort */
  43.  #define SOH      1       /* Start of sector char */
  44.  #define EOT      4      /* end of transmission char */
  45.  #define ACK      6     /* acknowledge sector transmission */
  46.  #define NAK      21   /* error in transmission detected */
  47.  #define CAN      'X'&0x1f
  48.  #define CR       13
  49.  #define BELL    'G'&0x1f
  50.  #define CPM_EOF 'Z'&0x1f    /*CP/M end of file character */
  51.  static char
  52.      bufr[BufSize], numstring[5];
  53.  static int
  54.      fd,
  55.      timeout = FALSE,
  56.      cancel,
  57.      LOCAL, ECHO;
  58.  static long
  59.      bytes_xferred;
  60.  
  61.  UBYTE ModemRead();  /* C demands this non-integer declaration */
  62.                      /* 'cause ModemRead is used before it is defined */
  63.  
  64.  /*   Intuition always wants to see these declarations */
  65.  struct IntuitionBase *IntuitionBase;
  66.  struct GfxBase *GfxBase;
  67.  
  68.  /* my window structure */
  69.  struct NewWindow NewWindow = {
  70.     0,
  71.     0,
  72.     640,
  73.     200,
  74.     0,
  75.     1,
  76.     CLOSEWINDOW | RAWKEY | MENUPICK | NEWSIZE,
  77.     WINDOWCLOSE | SMART_REFRESH | ACTIVATE | WINDOWDEPTH | REPORTMOUSE
  78.     | BORDERLESS,
  79.     NULL,
  80.     NULL,
  81.     "AMIGA Terminal",
  82.     NULL,
  83.     NULL,
  84.     100, 35,
  85.     640, 200,
  86.     WBENCHSCREEN,
  87.  };
  88.  
  89.  struct Window *mywindow;             /* ptr to applications window */
  90.  struct IntuiMessage *NewMessage;    /* msg structure for GetMsg() */
  91.  
  92.  /*****************************************************
  93.  *                     File Menu
  94.  *****************************************************/
  95.  
  96.  /* define maximum number of menu items */
  97.  #define FILEMAX 4
  98.  
  99.  /*   declare storage space for menu items and
  100.   *   their associated IntuiText structures
  101.   */
  102.  struct MenuItem FileItem[FILEMAX];
  103.  struct IntuiText FileText[FILEMAX];
  104.  
  105.  /*****************************************************************/
  106.  /*    The following function initializes the structure arrays    */
  107.  /*   needed to provide the File menu topic.                      */
  108.  /*****************************************************************/
  109.  InitFileItems()
  110.  {
  111.  short n;
  112.  
  113.  /* initialize each menu item and IntuiText with loop */
  114.  for( n=0; n<FILEMAX; n++ )
  115.     {
  116.     FileItem[n].NextItem = &FileItem[n]+1;
  117.     FileItem[n].LeftEdge = 0;
  118.     FileItem[n].TopEdge = 11 * n;
  119.     FileItem[n].Width = 135;
  120.     FileItem[n].Height = 11;
  121.     FileItem[n].Flags = ITEMTEXT | ITEMENABLED | HIGHBOX;
  122.     FileItem[n].MutualExclude = 0;
  123.     FileItem[n].ItemFill = (APTR)&FileText[n];
  124.     FileItem[n].SelectFill = NULL;
  125.     FileItem[n].Command = 0;
  126.     FileItem[n].SubItem = NULL;
  127.     FileItem[n].NextSelect = 0;
  128.  
  129.     FileText[n].FrontPen = 0;
  130.     FileText[n].BackPen = 1;
  131.     FileText[n].DrawMode = JAM2;     /* render in fore and background */
  132.     FileText[n].LeftEdge = 0;
  133.     FileText[n].TopEdge = 1;
  134.     FileText[n].ITextFont = NULL;
  135.     FileText[n].NextText = NULL;
  136.     }
  137.  FileItem[FILEMAX-1].NextItem = NULL;
  138.  
  139.  /* initialize text for specific menu items */
  140.  FileText[0].IText = (UBYTE *)"Ascii Capture";
  141.  FileText[1].IText = (UBYTE *)"Ascii Send";
  142.  FileText[2].IText = (UBYTE *)"Xmodem Receive";
  143.  FileText[3].IText = (UBYTE *)"Xmodem Send";
  144.  
  145.  return( 0 );
  146.  }
  147.  
  148.  /*****************************************************/
  149.  /*                BaudRate  Menu                     */
  150.  /*****************************************************/
  151.  
  152.  /* define maximum number of menu items */
  153.  #define RSMAX 5
  154.  
  155.  /*   declare storage space for menu items and
  156.   *   their associated IntuiText structures
  157.   */
  158.  struct MenuItem RSItem[RSMAX];
  159.  struct IntuiText RSText[RSMAX];
  160.  
  161.  /*****************************************************************/
  162.  /*    The following function initializes the structure arrays    */
  163.  /*   needed to provide the BaudRate menu topic.                  */
  164.  /*****************************************************************/
  165.  InitRSItems()
  166.  {
  167.  short n;
  168.  
  169.  /* initialize each menu item and IntuiText with loop */
  170.  for( n=0; n<RSMAX; n++ )
  171.     {
  172.     RSItem[n].NextItem = &RSItem[n]+1;
  173.     RSItem[n].LeftEdge = 0;
  174.     RSItem[n].TopEdge = 11 * n;
  175.     RSItem[n].Width = 85;
  176.     RSItem[n].Height = 11;
  177.     RSItem[n].Flags = ITEMTEXT | ITEMENABLED | HIGHBOX | CHECKIT;
  178.     RSItem[n].MutualExclude = (~(1 << n));
  179.     RSItem[n].ItemFill = (APTR)&RSText[n];
  180.     RSItem[n].SelectFill = NULL;
  181.     RSItem[n].Command = 0;
  182.     RSItem[n].SubItem = NULL;
  183.     RSItem[n].NextSelect = 0;
  184.  
  185.     RSText[n].FrontPen = 0;
  186.     RSText[n].BackPen = 1;
  187.     RSText[n].DrawMode = JAM2;     /* render in fore and background */
  188.     RSText[n].LeftEdge = 0;
  189.     RSText[n].TopEdge = 1;
  190.     RSText[n].ITextFont = NULL;
  191.     RSText[n].NextText = NULL;
  192.     }
  193.  RSItem[RSMAX-1].NextItem = NULL;
  194.  /* 300 baud item chekced */
  195.  RSItem[0].Flags = ITEMTEXT | ITEMENABLED | HIGHBOX | CHECKIT | CHECKED;
  196.  
  197.  /* initialize text for specific menu items */
  198.  RSText[0].IText = (UBYTE *)"   300";
  199.  RSText[1].IText = (UBYTE *)"   1200";
  200.  RSText[2].IText = (UBYTE *)"   2400";
  201.  RSText[3].IText = (UBYTE *)"   4800";
  202.  RSText[4].IText = (UBYTE *)"   9600";
  203.  
  204.  return( 0 );
  205.  }
  206.  
  207.  /***************************************************
  208.  *                 Mode menu         (feb.86)
  209.  ****************************************************/
  210.  struct MenuItem ModeItem[3];
  211.  struct IntuiText Modetext[3];
  212.  
  213.  initmode()
  214.  {
  215.  short n;
  216.  
  217.  for ( n=0; n<3; n++)
  218.  
  219.  {
  220.   ModeItem[n].NextItem = &ModeItem[n]+1; /* Lattice 3.03 likes this syntax */
  221.   ModeItem[n].LeftEdge = 0;
  222.   ModeItem[n].TopEdge = 11 * n;
  223.   ModeItem[n].Width = 50;
  224.   ModeItem[n].Height = 11;
  225.   ModeItem[n].Flags = ITEMTEXT | ITEMENABLED | HIGHBOX | CHECKIT;
  226.   ModeItem[n].MutualExclude = (~(1 << n));
  227.   ModeItem[n].ItemFill = (APTR) &Modetext[n];
  228.   ModeItem[n].SelectFill = NULL;
  229.   ModeItem[n].Command = 0;
  230.   ModeItem[n].SubItem = NULL;
  231.   ModeItem[n].NextSelect = 0;
  232.  
  233.   Modetext[n].FrontPen = 0;
  234.   Modetext[n].BackPen = 1;
  235.   Modetext[n].DrawMode = JAM2;
  236.   Modetext[n].LeftEdge = 0;
  237.   Modetext[n].TopEdge = 1;
  238.   Modetext[n].ITextFont = NULL;
  239.   Modetext[n].NextText = NULL;
  240.  }
  241.  
  242.  ModeItem[2].NextItem = NULL;
  243.  
  244.  /* default is Terminal mode  (full duplex)  */
  245.  ModeItem[1].Flags = ITEMTEXT | ITEMENABLED | HIGHBOX | CHECKIT | CHECKED;
  246.  LOCAL = 0;
  247.  ECHO = 0;
  248.  
  249.  Modetext[0].IText = "    Half";
  250.  Modetext[1].IText = "    Full";
  251.  Modetext[2].IText = "    Echo";
  252.  
  253.  }     /* end initmode */
  254.  
  255.  
  256.  /***************************************************/
  257.  /*                Menu Definition                  */
  258.  /*                                                 */
  259.  /*      This section of code is where the simple   */
  260.  /*   menu definition goes.                         */
  261.  /***************************************************/
  262.  
  263.  /* current number of available menu topics */
  264.  #define MAXMENU 3
  265.  
  266.  /*   declaration of menu structure array for
  267.   *   number of current topics.  Intuition
  268.   *   will use the address of this array to
  269.   *   set and clear the menus associated with
  270.   *   the window.
  271.   */
  272.  struct Menu menu[MAXMENU];
  273.  
  274.  /**********************************************************************/
  275.  /*   The following function initializes the Menu structure array with */
  276.  /*  appropriate values for our simple menu strip.  Review the manual  */
  277.  /*  if you need to know what each value means.                        */
  278.  /**********************************************************************/
  279.  InitMenu()
  280.  {
  281.  menu[0].NextMenu = &menu[1];
  282.  menu[0].LeftEdge = 5;
  283.  menu[0].TopEdge = 0;
  284.  menu[0].Width = 50;
  285.  menu[0].Height = 10;
  286.  menu[0].Flags = MENUENABLED;
  287.  menu[0].MenuName = "File";           /* text for menu-bar display */
  288.  menu[0].FirstItem = &FileItem[0];    /* pointer to first item in list */
  289.  
  290.  menu[1].NextMenu = &menu[2];
  291.  menu[1].LeftEdge = 65;
  292.  menu[1].TopEdge = 0;
  293.  menu[1].Width = 85;
  294.  menu[1].Height = 10;
  295.  menu[1].Flags = MENUENABLED;
  296.  menu[1].MenuName = "BaudRate";        /* text for menu-bar display */
  297.  menu[1].FirstItem = &RSItem[0];    /* pointer to first item in list */
  298.  
  299.  menu[2].NextMenu = NULL;                   /* (feb.86) */
  300.  menu[2].LeftEdge = 150;
  301.  menu[2].TopEdge = 0;
  302.  menu[2].Width = 40;
  303.  menu[2].Height = 10;
  304.  menu[2].Flags = MENUENABLED;
  305.  menu[2].MenuName = "Mode";
  306.  menu[2].FirstItem = &ModeItem[0];
  307.  }
  308.  
  309.  /* declarations for the serial stuff */
  310.  extern struct MsgPort *CreatePort();
  311.  struct IOExtSer *Read_Request;
  312.  static char rs_in[2];
  313.  struct IOExtSer *Write_Request;
  314.  static char rs_out[2];
  315.  
  316.  /******************************************************/
  317.  /*                   Main Program                     */
  318.  /*                                                    */
  319.  /*      This is the main body of the program.         */
  320.  /******************************************************/
  321.  
  322.  main()
  323.  {
  324.  ULONG class;
  325.  USHORT code,menunum,itemnum;
  326.  int KeepGoing,capture,send;
  327.  char c,name[32];
  328.  FILE *tranr,*trans;
  329.  
  330.  IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition.library", INTUITION_REV);
  331.  if( IntuitionBase == NULL )
  332.     {
  333.     puts("can't open intuition\n");
  334.     exit(TRUE);
  335.     }
  336.  
  337.  GfxBase = (struct GfxBase *)OpenLibrary("graphics.library",GRAPHICS_REV);
  338.  if( GfxBase == NULL )
  339.     {
  340.     puts("can't open graphics library\n");
  341.     exit(TRUE);
  342.     }
  343.  
  344.  if(( mywindow = (struct Window *)OpenWindow(&NewWindow) ) == NULL)
  345.     {
  346.     puts("cant open window\n");
  347.     exit(TRUE);
  348.     }
  349.  
  350.  Read_Request = (struct IOExtSer *)AllocMem(sizeof(*Read_Request),MEMF_PUBLIC|MEMF_CLEAR);
  351.  Read_Request->io_SerFlags = SERF_SHARED;
  352.  Read_Request->io_CtlChar = 0x11130501;
  353.  Read_Request->IOSer.io_Message.mn_ReplyPort = CreatePort("Read_RS",0);
  354.  if(OpenDevice(SERIALNAME,NULL,Read_Request,NULL))
  355.     {
  356.     puts("Cant open Read device\n");
  357.     CloseWindow( mywindow );
  358.     DeletePort(Read_Request->IOSer.io_Message.mn_ReplyPort);
  359.     FreeMem(Read_Request,sizeof(*Read_Request));
  360.     exit(TRUE);
  361.     }
  362.  Read_Request->IOSer.io_Command = CMD_READ;
  363.  Read_Request->IOSer.io_Length = 1;
  364.  Read_Request->IOSer.io_Data = (APTR) &rs_in[0];
  365.  
  366.  Write_Request = (struct IOExtSer *)AllocMem(sizeof(*Write_Request),MEMF_PUBLIC|MEMF_CLEAR);
  367.  Write_Request->io_SerFlags = SERF_SHARED;
  368.  Write_Request->io_CtlChar = 0x11130501;
  369.  Write_Request->IOSer.io_Message.mn_ReplyPort = CreatePort("Write_RS",0);
  370.  if(OpenDevice(SERIALNAME,NULL,Write_Request,NULL))
  371.     {
  372.     puts("Cant open Write device\n");
  373.     CloseWindow( mywindow );
  374.     DeletePort(Write_Request->IOSer.io_Message.mn_ReplyPort);
  375.     FreeMem(Write_Request,sizeof(*Write_Request));
  376.     DeletePort(Read_Request->IOSer.io_Message.mn_ReplyPort);
  377.     FreeMem(Read_Request,sizeof(*Read_Request));
  378.     exit(TRUE);
  379.     }
  380.  Write_Request->IOSer.io_Command = CMD_WRITE;
  381.  Write_Request->IOSer.io_Length = 1;
  382.  Write_Request->IOSer.io_Data = (APTR) &rs_out[0];
  383.  
  384.  Read_Request->io_SerFlags = SERF_SHARED | SERF_XDISABLED;
  385.  Read_Request->io_Baud = 300;
  386.  Read_Request->io_ReadLen = 8;
  387.  Read_Request->io_WriteLen = 8;
  388.  Read_Request->io_CtlChar = 1L;
  389.  Read_Request->IOSer.io_Command = SDCMD_SETPARAMS;
  390.  DoIO(Read_Request);
  391.  Read_Request->IOSer.io_Command = CMD_READ;
  392.  
  393.  InitFileItems();
  394.  InitRSItems();
  395.  initmode();
  396.  InitMenu();
  397.  SetMenuStrip(mywindow,&menu[0]);
  398.  
  399.  KeepGoing = TRUE;
  400.  capture=FALSE;
  401.  send=FALSE;
  402.  SetAPen(mywindow->RPort,1);
  403.  emit(12);
  404.  
  405.  BeginIO(Read_Request);
  406.  
  407.  while( KeepGoing )
  408.       {
  409.                       /* wait for window message or serial port message */
  410.       if (send == 0)           /* don't wait for event when sending */
  411.       Wait((1 << Read_Request->IOSer.io_Message.mn_ReplyPort->mp_SigBit) | ( 1 << mywindow->UserPort->mp_SigBit));
  412.  
  413.       if (send)
  414.           {
  415.           if ((c=getc(trans)) != EOF)
  416.               {
  417.               if (c == '\n')  sendchar(CR);  /* add carriage return  */
  418.               sendchar(c);
  419.               }
  420.           else
  421.               {
  422.               fclose(trans);
  423.               emits("\nFile Sent\n");
  424.               send=FALSE;
  425.               }
  426.           }
  427.       if(CheckIO(Read_Request))
  428.         {
  429.          WaitIO(Read_Request);
  430.          c=rs_in[0] & 0x7f;
  431.          BeginIO(Read_Request);
  432.          emit(c);
  433.          if (ECHO)     /* simple echo, no LF after CR   (feb.86) */
  434.           {
  435.            rs_out[0] = c;       /* must do this way: sendchar() also */
  436.            DoIO(Write_Request); /* emit()s if LOCAL, and calling sendchar() */
  437.            }                    /* would do a double-emit() in EchoPlex */
  438.          if (capture)
  439.              if (c > 31 && c < 127 || c == '\n') /* assumes CR/LF sequence */
  440.                                     /* trash them mangy ctl chars */
  441.                  putc(c , tranr);
  442.          }
  443.  
  444.       while( NewMessage=(struct IntuiMessage *)GetMsg(mywindow->UserPort) )
  445.            {
  446.            class = NewMessage->Class;
  447.            code = NewMessage->Code;
  448.            ReplyMsg( NewMessage );
  449.            switch( class )
  450.                  {
  451.                  case CLOSEWINDOW:
  452.                     /*   User is ready to quit, so indicate
  453.                     *   that execution should terminate
  454.                     *   with next iteration of the loop.
  455.                     */
  456.                     KeepGoing = FALSE;
  457.                  break;
  458.  
  459.                  case RAWKEY:
  460.                  /*  User has touched the keyboard */
  461.                  switch( code )
  462.                        {
  463.                        case 95: /* help key */
  464.                           emits("ESC Aborts Xmodem Xfer\n");
  465.                           emits("Front/Back gadgets and Close gadget are still active though usneen.\n");
  466.                           emits("AMIGA Term 1.0\n");
  467.                           emits("Copyright 1985 by Michael Mounier\n");
  468.                        break;
  469.                        default:   
  470.                           c = toasc(code); /* get in into ascii */
  471.                           if (c != 0)
  472.                              {
  473.                               rs_out[0] = c;
  474.                               DoIO(Write_Request);
  475.                               if (LOCAL) emit(c);
  476.                               }
  477.                        break;
  478.                        }
  479.                  break;
  480.  
  481.                  case NEWSIZE:
  482.                     emit(12);
  483.                  break;
  484.  
  485.                  case MENUPICK:
  486.                     if ( code != MENUNULL )
  487.                         {
  488.                         menunum = MENUNUM( code );
  489.                         itemnum = ITEMNUM( code );
  490.                         switch( menunum )
  491.                               {
  492.                               case 0:
  493.                                  switch( itemnum )
  494.                                        {
  495.                                        case 0:
  496.                                           if (capture == TRUE)
  497.                                               {
  498.                                               capture=FALSE;
  499.                                               fclose(tranr);
  500.                                               emits("\nEnd File Capture\n");
  501.                                               }
  502.                                           else
  503.                                               {
  504.                                               emits("\nAscii Capture:");
  505.                                               filename(name);
  506.                                               if ((tranr=fopen(name,"w")) == 0)
  507.                                                   {
  508.                                                   capture=FALSE;
  509.                                                   emits("\nError Opening File\n");
  510.                                                   break;
  511.                                                   }
  512.                                               capture=TRUE;
  513.                                               }
  514.                                        break;
  515.                                        case 1:
  516.                                           if (send == TRUE)
  517.                                               { 
  518.                                               send=FALSE;
  519.                                               fclose(trans);
  520.                                               emits("\nFile Send Cancelled\n");
  521.                                               }
  522.                                           else
  523.                                               {
  524.                                               emits("\nAscii Send:");
  525.                                               filename(name);
  526.                                               if ((trans=fopen(name,"r")) == 0)
  527.                                                   {
  528.                                                   send=FALSE;
  529.                                                   emits("\nError Opening File\n");
  530.                                                   break;
  531.                                                   }
  532.                                               send=TRUE;
  533.                                               }
  534.                                        break;
  535.                                        case 2:
  536.                                           emits("\nXmodem Receive:");
  537.                                           filename(name);
  538.                                           if (XMODEM_Read_File(name))
  539.                                              {
  540.                                              emits("\nReceived File\n");
  541.                                              emit(BELL);
  542.                                              }
  543.                                           else
  544.                                              {
  545.                                              close(fd);
  546.                                              emits("Xmodem Receive Failed\n");
  547.                                              emit(BELL);
  548.                                              }
  549.                                        break;
  550.                                        case 3:
  551.                                           emits("\nXmodem Send:");
  552.                                           filename(name);
  553.                                           if (XMODEM_Send_File(name))
  554.                                               {
  555.                                               emits("\nSent File\n");
  556.                                               emit(BELL);
  557.                                               }
  558.                                           else
  559.                                               {
  560.                                               close(fd);
  561.                                               emits("\nXmodem Send Failed\n");
  562.                                               emit(BELL);
  563.                                               }
  564.                                        break;
  565.                                        }
  566.                               break;
  567.  
  568.                               case 1:
  569.                                  AbortIO(Read_Request);
  570.                                  switch( itemnum )
  571.                                        {
  572.                                        case 0:
  573.                                           Read_Request->io_Baud = 300;
  574.                                        break;
  575.                                        case 1:
  576.                                           Read_Request->io_Baud = 1200;
  577.                                        break;
  578.                                        case 2:
  579.                                           Read_Request->io_Baud = 2400;
  580.                                        break;
  581.                                        case 3:
  582.                                           Read_Request->io_Baud = 4800;
  583.                                        break;
  584.                                        case 4:
  585.                                           Read_Request->io_Baud = 9600;
  586.                                        break;
  587.                                        }
  588.                                  Read_Request->IOSer.io_Command = SDCMD_SETPARAMS;
  589.                                  DoIO(Read_Request);
  590.                                  Read_Request->IOSer.io_Command = CMD_READ;
  591.                                  BeginIO(Read_Request);
  592.                               break;
  593.                              case 2:                /*   "Mode" (feb.86) */
  594.                                switch( itemnum)
  595.                                {
  596.                                 case 0:  /* Half Duplex */
  597.                                  ECHO = 0;
  598.                                  LOCAL = 1;
  599.                                  break;
  600.                                 case 1:  /* Full Duplex */
  601.                                  ECHO = 0;
  602.                                  LOCAL = 0;
  603.                                  break;
  604.                                 case 2:  /* EchoPlex  */
  605.                                  ECHO = 1;
  606.                                  LOCAL = 1;
  607.                                 }
  608.                               } /* end of switch ( menunum ) */
  609.                         }    /*  end of if ( not null ) */
  610.                  }   /* end of switch (class) */
  611.            }   /* end of while ( newmessage )*/
  612.       }  /* end while ( keepgoing ) */
  613.  
  614.  /*   It must be time to quit, so we have to clean
  615.  *   up and exit.
  616.  */
  617.         
  618.  CloseDevice(Read_Request);
  619.  DeletePort(Read_Request->IOSer.io_Message.mn_ReplyPort);
  620.  FreeMem(Read_Request,sizeof(*Read_Request));
  621.  CloseDevice(Write_Request);
  622.  DeletePort(Write_Request->IOSer.io_Message.mn_ReplyPort);
  623.  FreeMem(Write_Request,sizeof(*Write_Request));
  624.  ClearMenuStrip( mywindow );
  625.  CloseWindow( mywindow );
  626.  exit(FALSE);
  627.  } /* end of main */
  628.  
  629.  /*************************************************
  630.  *  function to get file name
  631.  *************************************************/
  632.  
  633.  filename(name)
  634.  char name[];
  635.  {
  636.  char c;
  637.  ULONG class;
  638.  USHORT code;
  639.  int keepgoing,i;
  640.  keepgoing = TRUE;
  641.  i=0;
  642.  while (keepgoing) {
  643.        while( NewMessage=(struct IntuiMessage *)GetMsg(mywindow->UserPort) )
  644.             {
  645.             class = NewMessage->Class;
  646.             code = NewMessage->Code;
  647.             ReplyMsg( NewMessage );
  648.             if (class=RAWKEY)
  649.                {
  650.                c = toasc(code);
  651.                name[i]=c;
  652.                if (name[i] != 0)
  653.                   {
  654.                   if (name[i] == 13)
  655.                      {
  656.                      name[i]=0;
  657.                      keepgoing = FALSE;
  658.                      }
  659.                   else
  660.                      {
  661.                      if (name[i] == 8)
  662.                         {
  663.                         i -=2;
  664.                         if (i < -1)
  665.                            i = -1;
  666.                         else
  667.                            {
  668.                            emit(8);
  669.                            emit(32);
  670.                            emit(8);
  671.                            }
  672.                         }
  673.                      else
  674.                         emit(c);
  675.                      }
  676.                   i += 1;
  677.                   }
  678.                }
  679.            } /* end of new message loop */
  680.        }   /* end of god knows what */
  681.        emit(13);
  682.  } /* end of function */
  683.  
  684.  
  685.  /*************************************************
  686.  *  function to print a string
  687.  *************************************************/
  688.  emits(string)
  689.  char string[];
  690.  {
  691.  int i;
  692.  char c;
  693.  
  694.  i=0;
  695.  while (string[i] != 0)
  696.        {
  697.        c=string[i];
  698.        if (c == 10)
  699.           c = 13;
  700.        emit(c);
  701.        i += 1;
  702.        }
  703.  }
  704.  
  705.  sendchar(ch)
  706.    int ch;
  707.    {
  708.      rs_out[0] = ch;
  709.      DoIO(Write_Request);
  710.      if (LOCAL)  emit(ch);  /* if Half-Duplex or Echo-plex  (feb.86)  */
  711.     }
  712.  
  713.  /**************************************************************/
  714.  /* send char and read char functions for the xmodem function */
  715.  /************************************************************/
  716.  
  717.  /*    purge() is called whenever an error is detected.
  718.       Its function is to flush the input buffer, and wait
  719.       until the sender has finished sending a block.
  720.       This ensures that the sender will see the NAK
  721.       sent by the error routine.
  722.                 (feb.86)
  723.  */
  724.  
  725.  void purge(purgetime)
  726.      int purgetime;      /* number of ticks @ 50 per second */
  727.     {
  728.  
  729.      do  ModemRead(purgetime);
  730.      while (timeout == FALSE);       /* Wait for the line to clear */
  731.     }
  732.  
  733.  
  734.  
  735.       /* ModemSend() is simply sendchar() without local echo
  736.          (feb.86)   */
  737.  
  738.  void ModemSend(c)
  739.      UBYTE c;
  740.      { rs_out[0] = c;
  741.        DoIO(Write_Request);
  742.       }
  743.  
  744.  /*     ModemRead()  gets a character from the serial port within a
  745.        specified number of ticks (tick = 1/50 second). DateStamp()
  746.        returns a 3-byte integer date consisting of day, minute, ticks.
  747.        ModemRead() will function correctly as long as the date is set
  748.        and a day boundary isn't crossed.
  749.           (feb.86')
  750.  */
  751.  
  752.  
  753.  UBYTE ModemRead(MAXTICKS)
  754.     int MAXTICKS;              /* number of ticks @ 50 per second */
  755.    {
  756.     static int startime[3], endtime[3];
  757.     UBYTE c;
  758.   
  759.     timeout = FALSE;
  760.     DateStamp(startime);
  761.     do {
  762.         if (NewMessage = (struct IntuiMessage *)GetMsg(mywindow->UserPort))
  763.          { 
  764.            if (NewMessage->Class == RAWKEY)  /* test for user cancelling */
  765.             {
  766.              if ((NewMessage->Code) == 69)     /* escape key pressed */
  767.               { cancel = TRUE;
  768.                 return (0);  /* This will tend to force an error        */
  769.                }             /* when <esc> pressed 'midst block receive */
  770.              }
  771.           }
  772.         if (CheckIO(Read_Request))
  773.          { WaitIO(Read_Request);
  774.            c = rs_in[0];
  775.            BeginIO(Read_Request);
  776.            return c;             /* Character came in on time */
  777.           }
  778.         DateStamp(endtime);
  779.         endtime[2] -= startime[2];
  780.          if ( endtime[1] -= startime[1] ) /* did minutes count change? */
  781.          endtime[2] += endtime[1] * 3000; /* number of ticks in minute */
  782.        }
  783.     while (endtime[2] < MAXTICKS);
  784.       /* else character not received on time */
  785.     timeout = TRUE;
  786.     return (0);
  787.    }
  788.  
  789.  
  790.  
  791.  /*   CloseEmUp() closes a receive file, ending Xmodem receive.
  792.       It also performs a 'chop' function, testing for
  793.       zero, 0xff, and CP/M_EOF (ctrol-z).
  794.       To implement the chop function, a two-block buffer
  795.       is maintained, with the basic pointer being bufr[offset],
  796.       with offset toggled 0,SECSIZ,0, etc.
  797.       During file receive, writes lag reads by one block: e.g.,
  798.       when block # 3 is received, block # 2 is written.
  799.       If block # 3 is the final block, CloseEmUp() will
  800.       chop any invalid characters, write the block, and
  801.       close the file.          (feb.86)
  802.  */
  803.  
  804.  CloseEmUp(fd,bufr,offset,ourblock)
  805.  
  806.  int offset, ourblock;
  807.  UBYTE *bufr;
  808.  
  809.  {
  810.   int i;
  811.   UBYTE c;
  812.  
  813.   offset = offset ? 0 : SECSIZ ;  /* toggle offset */
  814.  
  815.   if (ourblock)                     /* skip if no data at all */
  816.    {
  817.      i = SECSIZ - 1;
  818.      c = bufr[offset + i];       /* c gets last char in block */
  819.      if ( (c == 0) || (c == CPM_EOF) || (c == 0xff) )
  820.        {
  821.          while (bufr[ offset+(--i) ] == c)  /* Chop those useless bytes! */
  822.                     ;
  823.         }
  824.      if ((c = write(fd, bufr+offset, ++i)) != i)
  825.         emits("Error writing final block\n");
  826.      stcu_d(numstring, SECSIZ-i, 4);
  827.      emits(numstring);
  828.      emits(" bytes chopped from final block\n");
  829.     }
  830.  
  831.   close(fd);
  832.  }
  833.  
  834.  
  835.  /**************************************/
  836.  /* xmodem send and receive functions */
  837.  /************************************/
  838.  
  839.              /* These time constants are for benifit of
  840.                 CompuServe and other packet-switching networks
  841.                 that must be allowed more "slack time"
  842.                 due to inherent delays in system.
  843.               */
  844.  
  845.  #define Blocktime 20*50 /* Wait 20 seconds for start of block */
  846.  #define Chartime   2*50 /* Wait 2 seconds for chars when getting block */
  847.  
  848.  
  849.  XMODEM_Read_File(file)                /* (feb.86) */
  850.  
  851.  char *file;
  852.  
  853.  {
  854.  extern int timeout;
  855.  int blockin, ourblock, errors, checksum, offset, x,i; /*NEWCODE*/
  856.  unsigned char c;
  857.  
  858.  if ( (fd = creat(file,0)) < 0)
  859.     { emits("Cannot open file\n");
  860.       return FALSE;
  861.      }
  862.  else emits("Receiving file \n");
  863.  
  864.           /* Input file now open */
  865.  
  866.  offset = ourblock = errors = cancel = 0;
  867.  
  868.  ModemSend(NAK);
  869.                          /*  MAIN LOOP STARTS HERE  */
  870.  loop:
  871.  
  872.  c = ModemRead(Blocktime);
  873.  
  874.     if (cancel)
  875.     { emits("Xmodem cancelled\n");
  876.       close(fd);
  877.       purge(Chartime);
  878.       ModemSend(CAN);
  879.       return(FALSE);
  880.      }
  881.  
  882.     if (timeout)
  883.     { emits("timed out waiting for block start\n");
  884.       goto error;
  885.      }
  886.  
  887.  switch (c)
  888.   {
  889.     case (CAN):    /*Sender just quit*/
  890.     { purge(Chartime);
  891.       emits("Sender aborted transfer\n");
  892.       CloseEmUp(fd,bufr,offset,ourblock);
  893.       return(FALSE);
  894.      }
  895.     case (EOT):    /*Sender is finished */
  896.     {
  897.       CloseEmUp(fd,bufr,offset,ourblock);   /* chop and write last block */
  898.       emits("Transfer Completed\n");
  899.       ModemSend(ACK);
  900.       return(TRUE);
  901.      }
  902.     case (! SOH):
  903.     { purge(Chartime);
  904.       emits("not a valid header\n");
  905.       goto error;
  906.      }
  907.     default:   /*Sender has a block for us */
  908.     break;    /* block receive follows here */
  909.    }
  910.  blockin = ModemRead(Chartime);  /* get block number */
  911.     if (timeout)
  912.     { purge(Chartime);
  913.       emits("Didn't get block# in time\n");
  914.       goto error;
  915.      }
  916.  c = ModemRead(Chartime);       /* get 1/blocknumber */
  917.     if (timeout)
  918.     { purge(Chartime);
  919.       emits("Timed out waiting for 1/block#\n");
  920.       goto error;
  921.      }
  922.  
  923.  if ((c + blockin) != 255)  /* block # got lunched */
  924.     { purge(Chartime);
  925.       emits("block complement didn't match\n");
  926.       goto error;
  927.      }
  928.              /* Here comes a block of data  */
  929.  
  930.  checksum = 0;
  931.  
  932.  for (i = 0; i < SECSIZ; i++ )
  933.   {
  934.     c = ModemRead(Chartime);
  935.       if (timeout)
  936.        { purge(Chartime);
  937.          emits("Timed out during block\n");
  938.          goto error;
  939.         }
  940.     bufr[offset+i] = c;
  941.     checksum += c;
  942.    }
  943.  
  944.              /* Here comes the checksum */
  945.  c = ModemRead(Chartime);
  946.     if (timeout)
  947.     { purge(Chartime);
  948.       emits("Timed out waiting for checksum\n");
  949.       goto error;
  950.      }
  951.  if (c != (checksum & 0xff) ) /* checksums don't match */
  952.    {
  953.     purge(Chartime);
  954.     emits("checksum error\n");
  955.     goto error;
  956.     }
  957.  
  958.        /* Block has been received OK: Now check back on blocknumber */
  959.  
  960.  switch ( (char) (blockin - (ourblock & 0xff)) )
  961.  {
  962.   case (0):       /* Duplicate block */
  963.    {
  964.     emits("\nDuplicate block #  ");
  965.     stcu_d(numstring,ourblock,4);
  966.     emits(numstring);
  967.     emit( '\r' );
  968.     errors = 0;
  969.     ModemSend(ACK);
  970.     goto loop;
  971.    }
  972.   case (1):      /* New block */
  973.    {
  974.     ourblock++;           /* Update our block count */
  975.     emits("\vReceived block # ");
  976.     stcu_d(numstring,ourblock,4);
  977.     emits(numstring);
  978.     emits("     \n" );
  979.  
  980.     offset = offset ? 0 : SECSIZ;                  /* toggle offset */
  981.     if (ourblock != 1)
  982.     {                                              /* Write it out */
  983.       if ( (x = (write(fd, bufr+offset, SECSIZ)) ) != SECSIZ)
  984.         { close(fd);
  985.           emits("Error writing to file.\n");
  986.           emits("Transfer aborted.\n");
  987.           ModemSend(CAN);
  988.           return(FALSE);
  989.          }
  990.      }
  991.     errors = 0;
  992.     ModemSend(ACK);
  993.     goto loop;
  994.    }
  995.  default:
  996.      break;     /* fall through to error: */
  997.  }
  998.  purge(Chartime);
  999.  emits("Block numbers didn't agree\n");
  1000.  
  1001.                    /* ERROR ROUTINE STARTS HERE */
  1002.  error:
  1003.  
  1004.  if (cancel)
  1005.     { emits("XModem cancelled\n");
  1006.       close(fd);
  1007.       ModemSend(CAN);
  1008.       return(FALSE);
  1009.      }
  1010.  
  1011.   errors++;
  1012.  
  1013.   if (errors == 10) /* then abort */
  1014.     {
  1015.       emits(" 10 Errors. Transfer cancelled.\n");
  1016.       close(fd);
  1017.       ModemSend(CAN);
  1018.       return(FALSE);
  1019.      }
  1020.   emits("Error # ");
  1021.   emit( errors + '0');
  1022.   emit( '\r' );
  1023.   ModemSend(NAK);
  1024.   goto loop;
  1025.  
  1026.  } /* end of XMODEM_Read_File()  */
  1027.  
  1028.  
  1029.  #define WaitTime 20*50  /* Wait this long for NAKs (20 seconds)*/
  1030.  
  1031.  XMODEM_Send_File(file)
  1032.      char *file;
  1033.  {
  1034.      int sectnum, bytes_to_send, size, attempts, c, i;
  1035.      unsigned checksum, j, bufptr;
  1036.      char numb[10];
  1037.  
  1038.      timeout=FALSE;
  1039.      bytes_xferred = 0;
  1040.      if ((fd = open(file, 1)) < 0)
  1041.          {
  1042.          emits("Cannot Open Send File\n");
  1043.          return FALSE;
  1044.          }
  1045.      else
  1046.          emits("Sending File\n");
  1047.      attempts = 0;
  1048.      sectnum = 1;
  1049.  /* wait for sync char */
  1050.      j=1;
  1051.      while (((c = ModemRead()) != NAK) && (j++ < ERRORMAX));
  1052.      if (j >= (ERRORMAX))
  1053.          {
  1054.          emits("\nReceiver not sending NAKs\n");
  1055.          return FALSE;
  1056.          };
  1057.  
  1058.      while ((bytes_to_send = read(fd, bufr, BufSize)) && attempts != RETRYMAX)
  1059.          {
  1060.          if (bytes_to_send == EOF)
  1061.              {
  1062.              emits("\nError Reading File\n");
  1063.              return FALSE;
  1064.              };
  1065.  
  1066.          bufptr = 0;
  1067.          while (bytes_to_send > 0 && attempts != RETRYMAX)
  1068.              {
  1069.              attempts = 0;
  1070.              do
  1071.                  {
  1072.                  ModemSend(SOH);
  1073.                  ModemSend(sectnum);
  1074.                  ModemSend(~sectnum);
  1075.                  checksum = 0;
  1076.                  size = SECSIZ <= bytes_to_send ? SECSIZ : bytes_to_send;
  1077.                  bytes_to_send -= size;
  1078.                  for (j = bufptr; j < (bufptr + SECSIZ); j++)
  1079.                      if (j < (bufptr + size))
  1080.                          {
  1081.                          ModemSend(bufr[j]);
  1082.                          checksum += bufr[j];
  1083.                          }
  1084.                      else
  1085.                          {
  1086.                          ModemSend(0);
  1087.                          }
  1088.                  ModemSend(checksum & 0xff);
  1089.                  attempts++;
  1090.                  c = ModemRead(WaitTime);
  1091.                  if (timeout == TRUE)
  1092.                     return FALSE;
  1093.                  }
  1094.              while ((c != ACK) && (attempts != RETRYMAX));
  1095.              bufptr += size;
  1096.              bytes_xferred += size;
  1097.              emits("Block ");
  1098.              stci_d(numb,sectnum,i);
  1099.              emits(numb);
  1100.              emits(" sent\n");
  1101.              sectnum++;
  1102.              }
  1103.          }
  1104.      close(fd);
  1105.      if (attempts == RETRYMAX)
  1106.          {
  1107.          emits("\nNo Acknowledgment Of Sector, Aborting\n");
  1108.          return FALSE;
  1109.          }
  1110.      else
  1111.          {
  1112.          attempts = 0;
  1113.          do
  1114.              {
  1115.              ModemSend(EOT);
  1116.              attempts++;
  1117.              }
  1118.          while ((ModemRead(WaitTime) != ACK) && (attempts != RETRYMAX) && (timeout == FALSE));
  1119.           if (attempts == RETRYMAX)
  1120.              emits("\nNo Acknowledgment Of End Of File\n");
  1121.          };
  1122.      return TRUE;
  1123.  }
  1124.  
  1125.  /*************************************************
  1126.  *  function to output ascii chars to window
  1127.  *************************************************/
  1128.  emit(c)
  1129.  char c;
  1130.  {
  1131.  static short x = 0;
  1132.  static short y = 0;
  1133.  short xmax,ymax,cx,cy;
  1134.  
  1135.  xmax = mywindow->Width;
  1136.  ymax = mywindow->Height;
  1137.  
  1138.  /* cursor */
  1139.  if (c != 0)
  1140.     {
  1141.     if (x > (xmax-3))
  1142.        {
  1143.        cx = 7;
  1144.        cy = y + 8;
  1145.        }
  1146.     else
  1147.       {
  1148.       cx = x+8;
  1149.       cy = y;
  1150.       }
  1151.   
  1152.     if (cy > (ymax-2))
  1153.        {
  1154.        cx = 7;
  1155.        cy -= 8;
  1156.        }
  1157.  
  1158.     SetDrMd(mywindow->RPort,COMPLEMENT);
  1159.     SetAPen(mywindow->RPort,3);
  1160.     RectFill(mywindow->RPort,cx-7,cy-6,cx,cy+1);
  1161.     SetAPen(mywindow->RPort,1);
  1162.     SetDrMd(mywindow->RPort,JAM2);
  1163.  
  1164.     if (x > (xmax-3))
  1165.        {
  1166.        x = 0;
  1167.        y += 8;
  1168.        }
  1169.  
  1170.     if (y > (ymax-2))
  1171.        {
  1172.        x = 0;
  1173.        y -= 8;
  1174.        }
  1175.  
  1176.     Move(mywindow->RPort,x,y);
  1177.  
  1178.     switch( c )
  1179.           {
  1180.           case '\t':
  1181.              x += 40;
  1182.              break;
  1183.           case '\n':
  1184.              break;
  1185.           case CR:  /* carriage return */
  1186.              x = 0;
  1187.              y += 8;
  1188.              break;
  1189.           case 8:   /* backspace */
  1190.              x -= 8;
  1191.              if (x < 1)
  1192.                 x = 0;
  1193.              break;
  1194.           case '\v':   /* vertical tab */
  1195.              y -=8;
  1196.              if ( y<14 )
  1197.                 y = 6;
  1198.              break;
  1199.           case 12:     /* page */
  1200.              x = 0;
  1201.              y = 6;
  1202.              SetAPen(mywindow->RPort,0);
  1203.              RectFill(mywindow->RPort,0,0,xmax,ymax);
  1204.              SetAPen(mywindow->RPort,1);
  1205.              break;
  1206.           case BELL:     /* bell */
  1207.              ClipBlit(mywindow->RPort,0,0,mywindow->RPort,0,0,xmax,ymax,0x50);
  1208.              ClipBlit(mywindow->RPort,0,0,mywindow->RPort,0,0,xmax,ymax,0x50);
  1209.              break;
  1210.           default:
  1211.              Text(mywindow->RPort,&c,1);
  1212.              x += 8;
  1213.           } /* end of switch */
  1214.  /* cursor */
  1215.     if (x > (xmax-3))
  1216.        {
  1217.        cx = 7;
  1218.        cy = y + 8;
  1219.        }
  1220.     else
  1221.       { 
  1222.       cx = x+8;
  1223.       cy = y;
  1224.       }
  1225.  
  1226.     if (cy > (ymax-2))
  1227.        {
  1228.        cx = 7;
  1229.        cy -= 8;
  1230.        ScrollRaster(mywindow->RPort,0,8,0,0,xmax,ymax);
  1231.        }
  1232.  
  1233.     SetAPen(mywindow->RPort,3);
  1234.     RectFill(mywindow->RPort,cx-7,cy-6,cx,cy+1);
  1235.     SetAPen(mywindow->RPort,1);
  1236.     }
  1237.  }    /* end if (c !=0) */
  1238.  
  1239.  /*************************************************
  1240.  *  function to take raw key data and convert it 
  1241.  *  into ascii chars
  1242.  **************************************************/
  1243.  toasc(code)
  1244.  USHORT code;
  1245.  {
  1246.  static int ctrl = FALSE;
  1247.  static int shift = FALSE;
  1248.  static int capsl = FALSE;
  1249.  char c;
  1250.  static char keys[75] = {   /* 'return' and 'enter' generate chr$(13) */
  1251.  '`','1','2','3','4','5','6','7','8','9','0','-'     /* 0x0b */,
  1252.  '=','\\',0,'0','q','w','e','r','t','y','u','i','o',  /* 0x18 */
  1253.  'p','[',']',0,'1','2','3','a','s','d','f','g','h',  /* 0x25 */
  1254.  'j','k','l',';','\'',0,0,'4','5','6', 0,'z','x','c','v',  /* 0x34 */
  1255.  'b','n','m',44,'.','/',0,'.' ,'7','8','9',' ',8,         /* 0x41 */
  1256.  '\t',13,13,27,127,0,0,0,'-' } ;
  1257.       /*         del              */
  1258.               switch ( code ) /* I didn't know about the Qualifier field when I wrote this */
  1259.                      {
  1260.                      case 98:
  1261.                         capsl = TRUE;
  1262.                         c = 0;
  1263.                         break;
  1264.                      case 226:
  1265.                         capsl = FALSE;
  1266.                         c = 0;
  1267.                         break;
  1268.                      case 99:
  1269.                         ctrl = TRUE;
  1270.                         c = 0;
  1271.                         break;
  1272.                      case 227:
  1273.                         ctrl = FALSE;
  1274.                         c = 0;
  1275.                         break;
  1276.                      case 96:
  1277.                      case 97:
  1278.                         shift = TRUE;
  1279.                         c = 0;
  1280.                         break;
  1281.                      case 224:
  1282.                      case 225:
  1283.                         shift = FALSE;
  1284.                         c = 0;
  1285.                         break;
  1286.                     default:
  1287.                         if (code < 75)
  1288.                            c = keys[code];
  1289.                         else
  1290.                            c = 0;
  1291.                         }
  1292.           
  1293.  /* add modifiers to the keys */
  1294.  
  1295.  if (c != 0)
  1296.     {
  1297.  
  1298.     if (ctrl && (c <= 'z') && (c >= 'a'))
  1299.        c -= 96;
  1300.     else if (shift)
  1301.             {
  1302.        if ((c <= 'z') && (c >= 'a'))
  1303.           c -= 32;
  1304.        else
  1305.           switch( c )
  1306.                 {
  1307.            case '[':
  1308.               c = '{';
  1309.               break;
  1310.            case ']':
  1311.               c = '}';
  1312.               break;
  1313.            case '\\':
  1314.               c = '|';
  1315.               break;
  1316.            case '\'':
  1317.               c = '"';
  1318.               break;
  1319.            case ';':
  1320.               c = ':';
  1321.               break;
  1322.            case '/':
  1323.               c = '?';
  1324.               break;
  1325.            case '.':
  1326.               c = '>';
  1327.               break;
  1328.            case ',':
  1329.               c = '<';
  1330.               break;
  1331.            case '`':
  1332.               c = '~';
  1333.               break;
  1334.            case '=':
  1335.               c = '+';
  1336.               break;
  1337.            case '-':
  1338.               c = '_';
  1339.               break;
  1340.            case '1':
  1341.               c = '!';
  1342.               break;
  1343.            case '2':
  1344.               c = '@';
  1345.               break;
  1346.            case '3':
  1347.               c = '#';
  1348.               break;
  1349.            case '4':
  1350.               c = '$';
  1351.               break;
  1352.            case '5':
  1353.               c = '%';
  1354.               break;
  1355.            case '6':
  1356.               c = '^';
  1357.               break;
  1358.            case '7':
  1359.               c = '&';
  1360.               break;
  1361.            case '8':
  1362.               c = '*';
  1363.               break;
  1364.            case '9':
  1365.               c = '(';
  1366.               break;
  1367.            case '0':
  1368.               c = ')';
  1369.               break;
  1370.            default:
  1371.            } /* end switch */
  1372.        } /* end shift */
  1373.     else if (capsl && (c <= 'z') && (c >= 'a'))
  1374.             c -= 32;
  1375.     } /* end modifiers */
  1376.     return(c);
  1377.  } /* end of routine */
  1378.  /* end of file */
  1379.